package me.zhengjie.minbearwcs.serivce.outbound;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import me.zhengjie.minbearwcs.dao.mission.inf.WcsMissionDao;
import me.zhengjie.minbearwcs.dao.outbound.inf.OutboundOrderDao;
import me.zhengjie.minbearwcs.dao.outbound.inf.OutboundTaskRecordDao;
import me.zhengjie.minbearwcs.dao.outbound.inf.SortPalletDetailDao;
import me.zhengjie.minbearwcs.dao.stock.inf.MaterialStockDao;
import me.zhengjie.minbearwcs.dao.stock.inf.MaterialTypeDao;
import me.zhengjie.minbearwcs.dao.stock.inf.PalletDao;
import me.zhengjie.minbearwcs.dao.storage.inf.LargeStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.MediumStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.SmallStorageDao;
import me.zhengjie.minbearwcs.dao.storage.inf.StorageDao;
import me.zhengjie.minbearwcs.entity.DTO.outbound.pda.BoxMessage;
import me.zhengjie.minbearwcs.entity.DTO.outbound.pda.OutboundPdaContentPool;
import me.zhengjie.minbearwcs.entity.DTO.outbound.pda.RequiredSortingMessage;
import me.zhengjie.minbearwcs.entity.DTO.outbound.pda.SortMessage;
import me.zhengjie.minbearwcs.entity.DTO.response.RespWithObject;
import me.zhengjie.minbearwcs.entity.DTO.transfer.Tuple;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.WcsMission;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.sort.SortPalletDetail;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.status.OutboundSortMissionStatus;
import me.zhengjie.minbearwcs.entity.PO.wcs.mission.type.MissionType;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.common.OutboundContentPool;
import me.zhengjie.minbearwcs.entity.PO.wcs.outbound.record.OutboundTaskRecord;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.MaterialStock;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.MaterialType;
import me.zhengjie.minbearwcs.entity.PO.wcs.stock.Pallet;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.SmallStorage;
import me.zhengjie.minbearwcs.entity.PO.wcs.storage.type.StorageType;
import me.zhengjie.minbearwcs.serivce.mission.generate.MissionGeneratorCollector;
import me.zhengjie.minbearwcs.serivce.mission.generate.type.MissionGenType;
import me.zhengjie.minbearwcs.utils.IdWorker;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestBody;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
@RequiredArgsConstructor
public class OutboundPdaService {

    private final OutboundOrderDao outboundOrderDao;
    private final OutboundTaskRecordDao outboundTaskRecordDao;
    private final WcsMissionDao wcsMissionDao;
    private final MaterialStockDao materialStockDao;
    private final MaterialTypeDao materialTypeDao;
    private final SmallStorageDao smallStorageDao;
    private final IdWorker idWorker;
    private final PalletDao palletDao;
    private final MissionGeneratorCollector missionGeneratorCollector;
    private final StorageDao storageDao;
    private final MediumStorageDao mediumStorageDao;
    private final LargeStorageDao largeStorageDao;
    private final SortPalletDetailDao sortPalletDetailDao;

    @Transactional
    public boolean sortOutboundOrder(List<String> outboundOrderList) {
        for (int i = 0; i < outboundOrderList.size(); i++) {
            outboundOrderDao.updatePriorityByOutboundOrderCode(outboundOrderList.get(i), i);
        }
        return true;
    }


    public Tuple<RequiredSortingMessage> sortOutboundPallet(String palletCode) {
        List<OutboundTaskRecord> outboundTaskRecordList = outboundTaskRecordDao.selectSortPalletByPalletCode(palletCode, OutboundContentPool.OUTBOUND_RECORD_SORT);
        OutboundTaskRecord realRecord = null;

        for (OutboundTaskRecord outboundTaskRecord : outboundTaskRecordList) {
            WcsMission wcsMission = wcsMissionDao.selectWcsMission(
                    outboundTaskRecord.getOutboundOrderCode(),
                    MissionType.OUTBOUND_SORT.name(),
                    OutboundSortMissionStatus.WAIT_FOR_SORT.name(),
                    outboundTaskRecord.getOutboundRecordId()
            );
            if (wcsMission != null) {
                realRecord = outboundTaskRecord;
                break;
            }
        }

        if (realRecord != null) {
            MaterialType materialType = materialTypeDao.selectByMaterialCode(realRecord.getMaterialCode());
            RequiredSortingMessage requiredSortingMessage = new RequiredSortingMessage(
                    realRecord.getPalletCode(),
                    realRecord.getMaterialCode(),
                    materialType.getMaterialName(),
                    realRecord.getMaterialNum()
            );
            return Tuple.tuple(requiredSortingMessage, true);
        }

        return Tuple.tuple(null, false);
    }

    public Tuple<BoxMessage> getBoxMessage(String boxCode) {
        MaterialStock materialStock = materialStockDao.selectBySmallBoxCode(boxCode);
        if (materialStock != null) {
            BoxMessage boxMessage = new BoxMessage(boxCode, OutboundPdaContentPool.BOX_CODE_SMALL, new ArrayList<>(), 1);
            return Tuple.tuple(boxMessage, true);
        }

        List<MaterialStock> materialStockList = materialStockDao.selectByMidiumBoxCode(boxCode);
        if (!materialStockList.isEmpty()) {
            List<String> collect = materialStockList.stream()
                    .map(MaterialStock::getSmallBoxCode)
                    .collect(Collectors.toList());
            BoxMessage boxMessage = new BoxMessage(boxCode, OutboundPdaContentPool.BOX_CODE_MEDIUM, collect, materialStockList.size());
            return Tuple.tuple(boxMessage, true);
        }

        return Tuple.tuple(null, false);
    }


    @Transactional
    public Tuple<String> submitSortMessage(SortMessage sortMessage) {
        List<OutboundTaskRecord> outboundTaskRecordList = outboundTaskRecordDao.selectSortPalletByPalletCode(
                sortMessage.getPalletCode(),
                OutboundContentPool.OUTBOUND_RECORD_SORT
        );
        OutboundTaskRecord realRecord = null;
        WcsMission wcsMission = null;

        for (OutboundTaskRecord outboundTaskRecord : outboundTaskRecordList) {
            wcsMission = wcsMissionDao.selectWcsMission(
                    outboundTaskRecord.getOutboundOrderCode(),
                    MissionType.OUTBOUND_SORT.name(),
                    OutboundSortMissionStatus.WAIT_FOR_SORT.name(),
                    outboundTaskRecord.getOutboundRecordId()
            );
            if (wcsMission != null) {
                realRecord = outboundTaskRecord;
                break;
            }
        }

        if (realRecord == null) {
            return Tuple.tuple("没找到分拣信息", false);
        }

        if (wcsMission.getEnumMissionStatus() != OutboundSortMissionStatus.WAIT_FOR_SORT) {
            return Tuple.tuple("车体未到位，请车体到位再绑定", false);
        }


        // 判断数量对不对
        if (!Objects.equals(sortMessage.getSortNum(), realRecord.getMaterialNum())) {
            return Tuple.tuple("分拣数量不正确", false);
        }

        // 绑定栈板
        //地标找栈板
        SmallStorage bindStorage = smallStorageDao.selectByQrCode(sortMessage.getBindingStorage());
        if (bindStorage.getOutboundFreezePallet() != null) {
            return Tuple.tuple("栈板已生成出库任务，不能绑定", false);
        }


        String bindPallet = null;
        if (bindStorage.getPalletCode() == null) {
            //生成新的栈板
            bindPallet = "Faker_Pallet_" + idWorker.nextId();
            Pallet fakerPallet = new Pallet(null, bindPallet, null, null, null, null,
                    null, null, null, null,
                    realRecord.getLogisticsCompanyCode(), OutboundPdaContentPool.IS_FAKER_PALLET,null);
            palletDao.insert(fakerPallet);
            smallStorageDao.updatePalletCodeByQrPoint(sortMessage.getBindingStorage(),bindPallet);
        } else {
            bindPallet = bindStorage.getPalletCode();
        }

        // 更新物料栈板
        for (String smallBoxCode : sortMessage.getSmallBoxCodeList()) {
            materialStockDao.updatePalletCodeBySmallBoxCode(smallBoxCode, bindPallet);
            materialStockDao.updateMidiumBoxCodeBySmallBoxCode(smallBoxCode, null);
        }

        for (String mediumBoxCode : sortMessage.getMediumBoxCodeList()) {
            materialStockDao.updatePalletCodeByMidiumBoxCode(mediumBoxCode, bindPallet);
        }

        // 记录分拣信息
        sortPalletDetailDao.insert(
                new SortPalletDetail(null, bindPallet, realRecord.getOutboundOrderCode(), realRecord.getMaterialCode(),
                        sortMessage.getSortNum(), sortMessage.getPalletCode())
        );


        // 减原栈板数量
        Pallet pallet = palletDao.selectByPalletCode(sortMessage.getPalletCode());
        palletDao.updateMaterialNumByPalletCode(
                sortMessage.getPalletCode(),
                pallet.getMaterialNum() - sortMessage.getSortNum()
        );


        // 修改原栈板任务状态,退回栈板
        wcsMissionDao.updateMissionStatusByMissionId(
                wcsMission.getMissionId(),
                OutboundSortMissionStatus.WAIT_FOR_DISPATCH_MANTISSA_AREA_5F_STORAGE.name()
        );


        return Tuple.tuple(null, true);
    }


    public RespWithObject<String> sortAfterCallAgv(String qrPoint) {
        Tuple<String> generate = missionGeneratorCollector.getGenerator(MissionGenType.OUTBOUND_SORT_AFTER).generate(qrPoint);
        return new RespWithObject<String>()
                .code(generate.success)
                .reason(generate.value);
    }


    @Transactional
    public Tuple<String> clearStorage(String storageCode) {
        StorageType nowStorageType = storageDao.selectLargeStorageTypeByQrPoint(storageCode);

        HashSet<StorageType> canClearSet = new HashSet<>();
        canClearSet.add(StorageType.STORAGE_AREA_3F);
        canClearSet.add(StorageType.STORAGE_AREA_4F);
        canClearSet.add(StorageType.LIFT_EXIT_1F);
        canClearSet.add(StorageType.WHARF_1F);
        canClearSet.add(StorageType.LOADING_AREA_2F);

        if (!canClearSet.contains(nowStorageType)){
            return Tuple.tuple("该类型储位不能被清理",false);
        }

        SmallStorage smallStorage = smallStorageDao.selectByQrCode(storageCode);

        if (smallStorage.getInboundFreezePallet() != null) {
            return Tuple.tuple("栈板： " + smallStorage.getInboundFreezePallet() + "，正在入库到该储位，不能清空储位", false);
        }

        if (smallStorage.getOutboundFreezePallet() != null) {
            return Tuple.tuple("栈板： " + smallStorage.getOutboundFreezePallet() + "，正在从储位出库，不能清空储位", false);
        }

        smallStorageDao.updatePalletCodeByQrPoint(storageCode, null);

        StorageType storageType = storageDao.selectLargeStorageTypeByQrPoint(storageCode);
        if (StorageType.WHARF_1F == storageType) {
            String largeStorageCode = storageDao.selectLargeStorageCodeBySmallStorageCode(smallStorage.getSmallStorageCode());
            Integer numInLargeStorage = storageDao.selectPalletNumInLargeStorage(largeStorageCode);

            if (numInLargeStorage == 0) {
                largeStorageDao.updateLogisticsCompanyByLargeStorage(
                        largeStorageCode, null
                );
            }
        }

        return Tuple.tuple(null, true);

    }



    @Transactional
    public Tuple<String> sortExceptionCallAgv(String palletCode) {
        List<OutboundTaskRecord> outboundTaskRecordList = outboundTaskRecordDao.selectSortPalletByPalletCode(
                palletCode,
                OutboundContentPool.OUTBOUND_RECORD_SORT
        );
        OutboundTaskRecord realRecord = null;
        WcsMission wcsMission = null;

        for (OutboundTaskRecord outboundTaskRecord : outboundTaskRecordList) {
            wcsMission = wcsMissionDao.selectWcsMission(
                    outboundTaskRecord.getOutboundOrderCode(),
                    MissionType.OUTBOUND_SORT.name(),
                    OutboundSortMissionStatus.WAIT_FOR_SORT.name(),
                    outboundTaskRecord.getOutboundRecordId()
            );
            if (wcsMission != null) {
                realRecord = outboundTaskRecord;
                break;
            }
        }

        if (realRecord == null) {
            return Tuple.tuple("没找到该栈板信息", false);
        }

        if (wcsMission.getEnumMissionStatus() != OutboundSortMissionStatus.WAIT_FOR_SORT) {
            return Tuple.tuple("车体未到位，请车体到位叫车", false);
        }


        // 修改原栈板任务状态,退回栈板
        wcsMissionDao.updateMissionStatusByMissionId(
                wcsMission.getMissionId(),
                OutboundSortMissionStatus.WAIT_FOR_DISPATCH_EXCEPTION_AREA.name()
        );


        return Tuple.tuple(null, true);
    }



    @Transactional
    public Tuple<String> sortContinueCallAgv(String palletCode) {
        List<OutboundTaskRecord> outboundTaskRecordList = outboundTaskRecordDao.selectSortPalletByPalletCode(
                palletCode,
                OutboundContentPool.OUTBOUND_RECORD_SORT
        );
        OutboundTaskRecord realRecord = null;
        WcsMission wcsMission = null;

        for (OutboundTaskRecord outboundTaskRecord : outboundTaskRecordList) {
            wcsMission = wcsMissionDao.selectWcsMission(
                    outboundTaskRecord.getOutboundOrderCode(),
                    MissionType.OUTBOUND_SORT.name(),
                    OutboundSortMissionStatus.WAIT_FOR_CHECK_EXCEPTION.name(),
                    outboundTaskRecord.getOutboundRecordId()
            );
            if (wcsMission != null) {
                realRecord = outboundTaskRecord;
                break;
            }
        }

        if (realRecord == null) {
            return Tuple.tuple("没找到该栈板信息", false);
        }

        if (wcsMission.getEnumMissionStatus() != OutboundSortMissionStatus.WAIT_FOR_CHECK_EXCEPTION) {
            return Tuple.tuple("车体未到位，请车体到位叫车", false);
        }


        // 修改原栈板任务状态,退回栈板
        wcsMissionDao.updateMissionStatusByMissionId(
                wcsMission.getMissionId(),
                OutboundSortMissionStatus.WAIT_FOR_CONTINUE_SORT.name()
        );


        return Tuple.tuple(null, true);
    }


}
